import interfaces.IMap;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import javax.imageio.ImageIO;

import knowledgeSources.TestMainKnowledgeSource;

import KohonenNet.KohonenNet;
import Markers.BinMap;
import Markers.IContour;
import Markers.IContourSelector;
import Markers.Map;
import Markers.PointContour;
import Markers.RoundContourSelector;

import expertSystem.IKnowledgeBase;
import expertSystem.Knowledge;
import expertSystem.KnowledgeAssemblyLine;
import expertSystem.KnowledgeBase;
import expertSystem.KnowledgeType;
import filters.BigBlurFilter;
import filters.Binarizator;
import filters.Elimination;
import filters.LocalBinarizator;
import filters.BlurFilter;
import filters.DoubleMap;
import filters.EmbossFilter;
import filters.IFilter;
import filters.InvertFilter;
import filters.Maximazer;
import filters.MedianFilter;
import filters.ModaFilter;
import filters.SharpenFilter;
import filters.SobelFilter;
import filters.SubtractionFilter;

public class Main {

	
	//===========================================================================
	public static void main(String[] args) 
	{	
		/*Integer Model=10;
		KnowledgeBase KBase = new KnowledgeBase();
		TestMainKnowledgeSource MKSource = new TestMainKnowledgeSource("C:\\filter\\model"+Model.toString()+"\\model.jpg", 
				"C:\\filter\\model"+Model.toString()+"\\");
		
		KnowledgeAssemblyLine KAL = new KnowledgeAssemblyLine(MKSource, KBase);
		 
		
		KAL.Run();*/
		
		getKohonenMap();
		//ArrayList<IFilter> Filters =  new ArrayList<IFilter>();	
		//Filters.add( new MedianFilter(3));		
		//Filters.add( new SobelFilter());		
		//Filters.add( new LocalBinarizator(12));		
		//Filters.add( new InvertFilter());	
		//Filters.add( new MedianFilter(4));		
		//getFilter(8,Filters);
		//getContour(8,Filters);
		//getRect(1,Filters);
	}
	//===========================================================================
	public static void getKohonenMap()
	{
		Integer Model=10;
		BufferedImage img = getImageByLink("C:\\filter\\model"+Model.toString()+"\\model.jpg");
		
		Map oMap = new Map(img.getHeight(), img.getWidth());
		
		for (int y = 0; y < img.getHeight(); y++)
            for (int x = 0; x < img.getWidth(); x++)
            {
            	int buffer = img.getRGB(x, y);	
            	
            	oMap.Set(x,y,buffer);            	
            }
		
		KohonenNet oNet = new KohonenNet(15, 15, 15, oMap);
		oNet.Run(10000);
		
		IMap nMap = oNet.GetKohonenNetMap();
		BufferedImage imgN = new BufferedImage(nMap.Width(), nMap.Height(), BufferedImage.TYPE_INT_RGB);
		
		for (int y = 0; y < imgN.getHeight(); y++)
            for (int x = 0; x < imgN.getWidth(); x++)
            {
            	int buffer = nMap.Get(x, y);	
            	imgN.setRGB(x, y, buffer);            	       	
            }
		
		saveImageOnHardDrive(imgN, "C:\\filter\\model"+Model.toString()+"\\");
	}
	//===========================================================================
	public static void getFilter(Integer Model,ArrayList<IFilter> Filters)
	{
		BufferedImage img = getImageByLink("C:\\filter\\model"+Model.toString()+"\\model.jpg");
		
		
		BufferedImage imgN = BlackAndWhiteFilterS( img, Filters);
		saveImageOnHardDrive(imgN, "C:\\filter\\model"+Model.toString()+"\\");	
	}
	//===========================================================================
	public static void getContour(Integer Model, ArrayList<IFilter> Filters)
	{
		BufferedImage img = getImageByLink("C:\\filter\\model"+Model.toString()+"\\model.JPG");		
		
		DoubleMap Map = GetBlackAndWhiteMap(img);	
		
		for(int i=0;i<Filters.size();i++)
		{
			ApplyFilter(Map.First(), Map.Second(), Filters.get(i));	
			Map.Swap();
		}	
		
		IContourSelector Selector = new RoundContourSelector();
		BinMap binMap = copy(Map.First());
		ArrayList<IContour> Contours = Selector.Select(binMap);
		
		int H = img.getHeight();
		int W = img.getWidth();
		int N= 255;
		BufferedImage imgN = new BufferedImage(W, H, BufferedImage.TYPE_INT_RGB);
		for (IContour oContour : Contours) 
		{		
			if(oContour.Count()<50)
				continue;
			
			//Graphics Gr = imgN.getGraphics();
			//Gr.setColor(new Color(N));
			
			//Point startP = oContour.GetPoint(0);
			Point endP = null;
			for (int i = 1; i < oContour.Count(); i++) 
			{
				//if(i%8!=0)
					//continue;
				endP = oContour.GetPoint(i);			
				imgN.setRGB( endP.x, endP.y, (255 << 24) + (N << 16) + (N << 8) + N);
				//Gr.drawLine(startP.x, startP.y, endP.x, endP.y);
				//startP = endP;						            	            
			}	
			//endP = oContour.GetPoint(oContour.Count()-1);				
			//Gr.drawLine(startP.x, startP.y, endP.x, endP.y);
			//saveImageOnHardDrive(imgN, "C:\\filter\\model"+Model.toString()+"\\");	
		}		
		saveImageOnHardDrive(imgN, "C:\\filter\\model"+Model.toString()+"\\");	
	}	
	//===========================================================================
	private static BinMap copy(Map first) 
	{
		int H = first.Height();
		int W = first.Width();
		
		BinMap binMap = new BinMap(H, W);
		
		for (int y = 0; y < H; y++)
            for (int x = 0; x < W; x++)
            {
            	binMap.Set(x, y, first.Get(x, y));            
            }
		return binMap;
	}
	//===========================================================================	
	public static void getRect(Integer Model, ArrayList<IFilter> Filters)
	{
		BufferedImage img = getImageByLink("C:\\filter\\model"+Model.toString()+"\\model.JPG");		
		
		DoubleMap Map = GetBlackAndWhiteMap(img);	
		
		for(int i=0;i<Filters.size();i++)
		{
			ApplyFilter(Map.First(), Map.Second(), Filters.get(i));	
			Map.Swap();
		}	
		
		IContourSelector Selector = new RoundContourSelector();
		ArrayList<IContour> Contours = Selector.Select(Map.First());
		
		int H = img.getHeight();
		int W = img.getWidth();
		int N= (255 << 24) + (255 << 16) + (255 << 8) + 255;
		BufferedImage imgN = new BufferedImage(W, H, BufferedImage.TYPE_INT_RGB);
		Graphics Gr = imgN.getGraphics();
		Gr.setColor(new Color(N));
		for (IContour oContour : Contours) 
		{		
			if(oContour.Count()<50)
				continue;		
			
			for (int i = 1; i < oContour.Count(); i++) 
			{				
				PointContour PC = (PointContour)oContour;
				Gr.drawRect(PC.minX, PC.minY,PC.maxX - PC.minX, PC.maxY - PC.minY);									            	            
			}					
		}	
		saveImageOnHardDrive(imgN, "C:\\filter\\model"+Model.toString()+"\\");
	}
	//===========================================================================
	public static void mainSymbol() 
	{
		BufferedImage img = getImageByLink("C:\\filter\\model5\\model.bmp");
		ArrayList<IFilter> Filters =  new ArrayList<IFilter>();
		
		Filters.add( new MedianFilter(3));		
		Filters.add( new LocalBinarizator(80));
		Filters.add( new InvertFilter());	
		Filters.add( new MedianFilter(3));	
		Filters.add( new InvertFilter());
		
		DoubleMap Map = GetBlackAndWhiteMap(img);	
		
		for(int i=0;i<Filters.size();i++)
		{
			ApplyFilter(Map.First(), Map.Second(), Filters.get(i));	
			Map.Swap();
		}		
		
		Filters =  new ArrayList<IFilter>();
		
		Filters.add( new MedianFilter(3));		
		Filters.add( new SobelFilter());
		Filters.add( new MedianFilter(3));	
		//Filters.add( new Binarizator(20));
		Filters.add( new SubtractionFilter(Map.First(),80));		
		Filters.add( new LocalBinarizator(12));		
		
		BufferedImage imgN = BlackAndWhiteFilterS( img, Filters);
		
		
		saveImageOnHardDrive(imgN, "C:\\filter\\model5\\");		
	}
	//===========================================================================
	public static BufferedImage OldMainFilter(BufferedImage img)
	{
		//BufferedImage img = getImageByLink("C:\\filter\\model.jpg");
		int H = img.getHeight();
		int W = img.getWidth();
		BufferedImage imgN = new BufferedImage(W, H, BufferedImage.TYPE_INT_RGB);
		DoubleMap DMap = new DoubleMap(H,W);	
		
		for (int y = 0; y < H; y++)
            for (int x = 0; x < W; x++)
            {
            	int buffer = img.getRGB(x, y);
            	float b = (float)(buffer & 255);
            	float g = (float)((buffer >> 8) & 255);
            	float r = (float)((buffer >> 16) & 255);
            	int N = (int)getH(r,g,b);//(0.21 * r + 0.72 * g + 0.07 * b);
            	N = getRGB(N);
            	b = (float)(N & 255);
            	g = (float)((N >> 8) & 255);
            	r = (float)((N >> 16) & 255);            	
            	DMap.First().Set(x,y,(int)(0.21 * r + 0.72 * g + 0.07 * b));            	
            }
		
		/*MedianFilter BF = new MedianFilter();//BigBlurFilter BF = new BigBlurFilter();	//MedianFilter BF = new MedianFilter();//
		ApplyFilter(DMap.First(), DMap.Second(), BF);	
		DMap.Swap();*/
		
		//ModaFilter BF1 = new ModaFilter();//BigBlurFilter BF = new BigBlurFilter();	//MedianFilter BF = new MedianFilter();//
		//ApplyFilter(DMap.First(), DMap.Second(), BF1);	
		//DMap.Swap();
				
		SobelFilter filter = new SobelFilter();		
		ApplyFilter(DMap.First(), DMap.Second(), filter);	
		DMap.Swap();	
		
		//Maximazer Max = new Maximazer();		
		//ApplyFilter(DMap.First(), DMap.Second(), Max);	
		//DMap.Swap();
		
		LocalBinarizator binarizator = new LocalBinarizator(25);		
		ApplyFilter(DMap.First(), DMap.Second(), binarizator);	
		DMap.Swap();
		
		for (int y = 0; y < H; y++)
			for (int x = 0; x < W; x++)
			{
				int N = (int)DMap.First().Get(x, y);
				imgN.setRGB(x, y, (255 << 24) + (N << 16) + (N << 8) + N);
				//mapN.Set(x, y, filter.ApplyFilter(x,y, map));				            	            
			}

		return imgN;
	}
	//===========================================================================
	public static BufferedImage AndFilter(BufferedImage img,BufferedImage img1)
	{
		int H = img.getHeight();
		int W = img.getWidth();		
		
		BufferedImage imgN = new BufferedImage(W, H, BufferedImage.TYPE_INT_RGB);
		for (int y = 0; y < H; y++)
            for (int x = 0; x < W; x++)
            {
            	int buffer = img.getRGB(x, y);
            	
            	int buffer1 = img1.getRGB(x, y);
            	
            	imgN.setRGB(x, y,buffer&buffer1 ) ;       	
            }	
		return imgN;
	}
	//===========================================================================
	public static BufferedImage getHView(BufferedImage img,IFilter Filter)
	{
		BufferedImage imgN = new BufferedImage(img.getWidth(), img.getHeight(), BufferedImage.TYPE_INT_RGB);
		
		int offsets[] = new int[3];
		offsets[0] = 0;//b
		offsets[1] = 8;//g
		offsets[2] = 16;//r		
		
		DoubleMap[] Maps = GetRGBMaps(img, offsets);
		
		FilterMaps(Filter, Maps);
		
		for (int y = 0; y < img.getHeight(); y++)
            for (int x = 0; x < img.getWidth(); x++)
            {       
            	float b = Maps[0].First().Get(x, y);
            	float g = Maps[1].First().Get(x, y);
            	float r = Maps[2].First().Get(x, y);
            	int N = (int)getH(r,g,b);
            	N = getRGB(N); 
            	imgN.setRGB(x, y, N);
            }
		return imgN;
	}
	//===========================================================================
	public static BufferedImage RGBFilter(BufferedImage img,IFilter Filter)
	{
		
		DoubleMap Maps[];
		int offsets[] = new int[3];
		offsets[0] = 0;
		offsets[1] = 8;
		offsets[2] = 16;		
		
		Maps = GetRGBMaps(img, offsets);	
		
		FilterMaps(Filter, Maps);
		
		int H = img.getHeight();
		int W = img.getWidth();
		
		BufferedImage imgN = getImageFromMaps(Maps, offsets, H, W);

		return imgN;
	}
	//===========================================================================
	public static BufferedImage RGBFilterS(BufferedImage img,ArrayList<IFilter> Filters)
	{
		
		DoubleMap Maps[];
		int offsets[] = new int[3];
		offsets[0] = 0;
		offsets[1] = 8;
		offsets[2] = 16;		
		
		Maps = GetRGBMaps(img, offsets);	
		
		for(int i=0;i<Filters.size();i++)
			FilterMaps(Filters.get(i), Maps);
		
		int H = img.getHeight();
		int W = img.getWidth();
		
		BufferedImage imgN = getImageFromMaps(Maps, offsets, H, W);

		return imgN;
	}
	//===========================================================================	
	public static BufferedImage BlackAndWhiteFilterS(BufferedImage img,ArrayList<IFilter> Filters)
	{
		
		DoubleMap Map;	
		
		Map = GetBlackAndWhiteMap(img);	
		
		for(int i=0;i<Filters.size();i++)
		{
			ApplyFilter(Map.First(), Map.Second(), Filters.get(i));	
			Map.Swap();
		}
		
		int H = img.getHeight();
		int W = img.getWidth();
		
		DoubleMap Maps[] = new DoubleMap[3];
		Maps[0]=Map;
		Maps[1]=Map;
		Maps[2]=Map;
		int offsets[] = new int[3];
		offsets[0] = 0;
		offsets[1] = 8;
		offsets[2] = 16;	
		BufferedImage imgN = getImageFromMaps(Maps, offsets, H, W);

		return imgN;
	}
	//===========================================================================
	private static DoubleMap GetBlackAndWhiteMap(BufferedImage img) 
	{
		
		int H = img.getHeight();
		int W = img.getWidth();		
		DoubleMap DMap = new DoubleMap(H,W);	
		
		for (int y = 0; y < H; y++)
            for (int x = 0; x < W; x++)
            {
            	int buffer = img.getRGB(x, y);
            	
            	float b = (float)(buffer & 255);
            	float g = (float)((buffer >> 8) & 255);
            	float r = (float)((buffer >> 16) & 255);
            	int N = (int)(0.21 * r + 0.72 * g + 0.07 * b);
            	
            	DMap.First().Set(x,y,N);            	
            }
		return DMap;
	}
	//===========================================================================
	private static BufferedImage getImageFromMaps(DoubleMap[] Maps,	int[] offsets, int H, int W) 
	{
		BufferedImage imgN = new BufferedImage(W, H, BufferedImage.TYPE_INT_RGB);
		
		for (int y = 0; y < H; y++)
			for (int x = 0; x < W; x++)
			{
				int N=(255 << 24);
				
				for(int i=0;i<offsets.length;i++)				
					N+=((int) Maps[i].First().Get(x, y)) << offsets[i];		
				
				imgN.setRGB(x, y,  N);						            	            
			}
		return imgN;
	}
	//===========================================================================
	private static void FilterMaps(IFilter Filter, DoubleMap[] Maps) 
	{
		for(int i=0;i<Maps.length;i++)
		{					
			ApplyFilter(Maps[i].First(), Maps[i].Second(), Filter);	
			Maps[i].Swap();
		}
	}
	//===========================================================================
	private static DoubleMap[] GetRGBMaps(BufferedImage img,int[] offsets) 
	{
		DoubleMap[] Maps = new DoubleMap[offsets.length];
		for(int i=0;i<offsets.length;i++)
		{
			Maps[i] = getRGBMap(img,offsets[i]);			
		}
		return Maps;
	}
	//===========================================================================
	public static DoubleMap getRGBMap(BufferedImage img,int offset)
	{
		int H = img.getHeight();
		int W = img.getWidth();		
		DoubleMap DMap = new DoubleMap(H,W);	
		
		for (int y = 0; y < H; y++)
            for (int x = 0; x < W; x++)
            {
            	int buffer = img.getRGB(x, y);
            	
            	int P = ((buffer >> offset) & 255);
            	
            	DMap.First().Set(x,y,P);            	
            }
		return DMap;
	}
	//===========================================================================
	private static int getRGB(int H) 
	{
		int val = 255;
		int sat = 255;
		
		int R,G,B;
		R=0;
		G=0;
		B=0;
		while (H >= 360)
			H -= 360;
		
		int hue = H;

       int ii = (int)(hue / 60.0);

       int fr = hue - ii;

        int c1 = (val * (255 - sat)) / 255;

        int c2 = (val * (255 - sat * fr)) / 255;

        int c3 = (int)((val * (255 - sat * (1.0 - fr))) / 255);

        switch (ii)

          {

            case 0: R = val; G = c3; B = c1; break;

            case 1: R = c2; G = val; B = c1; break;

            case 2: R = c1; G = val; B = c3; break;

            case 3: R = c1; G = c2; B = val; break;

            case 4: R = c3; G = c1; B = val; break;

            case 5: R = val; G = c1; B = c2; break;

          } // switch
		
		return (255 << 24) + (R << 16) + (G << 8) + B;
	}
	//===========================================================================
	public static int getH(float r,float g,float b)
	{
		float H=0;
		float S=0;
		float V=0;
		float max=0;
		float min=0;
		if(r==b && b==g)
			return (int)(0.21 * r + 0.72 * g + 0.07 * b);
			
		float P=(float) 0.2;
		if(r>b && r>g)
		{
			max=r;
			if(g>=b)
			{
				min=b;
				H=60*((g-b)/(max-min));
			}
			else
			{
				min=g;
				H=(60*((g-b)/(max-min)))+360;
			}
		}
		else
		{
			if(g>b && g>r)
			{
				max=g;
				if(r>=b)
				{
					min=b;
					H=(60*((b-r)/(max-min)))+120;
				}
				else
				{
					min=r;
					H=(60*((b-r)/(max-min)))+120;
				}
			}
			else
			{
				if(b>g && b>r)
				{
					max=b;
					if(r>=g)
					{
						min=g;
						H=(60*((r-g)/(max-min)))+240;
					}
					else
					{
						min=r;
						H=(60*((r-g)/(max-min)))+240;//>P?1:0
					}
				}			
			}
		}
		if(max==0)
			S=0;
		else
		{
			S=(1-min/max)*255;
		}
		V=max;
		int oH=(int)H;
		int oS =(int)S;
		return oH;
	}
	//===========================================================================
	public static void ApplyFilter(IMap IN,IMap OUT,IFilter filter)
	{				
		for (int y = 0; y < IN.Height(); y++)
			for (int x = 0; x < IN.Width(); x++)
			{
				OUT.Set(x, y, filter.ApplyFilter(x,y, IN));				            	            
			}
	}
	//===========================================================================
	public static BufferedImage getImageByLink(String imageLink) 
	{		
		       BufferedImage img = null;
		       try 
		       {		
		           img = ImageIO.read(new File(imageLink));		
		       } 
		       catch (IOException e) 
		       {
		    	   System.out.println(" open error ");
		       }		
		       return img;		
	}
	//===========================================================================
	protected static String saveImageOnHardDrive(BufferedImage img, String pathDirectory) {
		
		        Date currentDate = new Date();
		
		        String pathOnTheCurrentFile = pathDirectory + currentDate.getTime() + "_copy.JPG";		
		 		
		        try 
		        {		
		            File outputFile = new File(pathOnTheCurrentFile);
		
		            ImageIO.write(img, "JPG", outputFile);		            
		        } 
		        catch (IOException e) 
		        {		
		        	System.out.println(" save error ");
		        }
		
		        return pathOnTheCurrentFile;		
		    }
	//===========================================================================
}
